package org.smart4j.framework.helper;

import cn.wenxin_zhang.smart4j.plus.proxy.TransactionAspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Aspect;
import org.smart4j.framework.proxy.AspectProxy;
import org.smart4j.framework.proxy.Proxy;
import org.smart4j.framework.proxy.ProxyManager;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * Created by Wenxin on 2017/3/5.
 */
public class AOPHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(AOPHelper.class);

    static {
        try {

            //加载ControllerAspect
            ClassHelper.loadClass("org.smart4j.framework.aspect.ControllerAspect");

            Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
            Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
            for (Map.Entry<Class<?>, List<Proxy>> entry : targetMap.entrySet()) {

                Class<?> targetClass = entry.getKey();
                List<Proxy> proxyList = entry.getValue();
                Object proxy = ProxyManager.createProxy(targetClass, proxyList);
                BeanHelper.setBean(targetClass,proxy);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取切面目标类
     *
     * @param aspect
     * @return
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) {
        Set<Class<?>> targetClassSet = new HashSet<>();

        Class<? extends Annotation> annotation = aspect.value();
        if (annotation != null && !annotation.equals(Aspect.class)) {
            targetClassSet.addAll(ClassHelper.getClassByAnnotation(annotation));
        }
        return targetClassSet;
    }

    /**
     * 获取切面和所有目标的映射
     *
     * @return
     * @throws Exception
     */
    private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception {
        Map<Class<?>, Set<Class<?>>> proxyMap = new HashMap<>();
//        获取切面类
        Set<Class<?>> proxyClassSet = ClassHelper.getClassBySuper(AspectProxy.class);
        for (Class<?> cls : proxyClassSet) {
            if (cls.isAnnotationPresent(Aspect.class)) {
                Aspect annotation = cls.getAnnotation(Aspect.class);
                Set<Class<?>> targetClassSet = createTargetClassSet(annotation);
                proxyMap.put(cls, targetClassSet);
            }
        }
        addTransactionClassSet(proxyMap);
        return proxyMap;
    }

    private static void addTransactionClassSet(Map<Class<?>, Set<Class<?>>> proxyMap) {
        proxyMap.put(TransactionAspect.class,ClassHelper.getServiceClassSet());
    }


    /**
     * 为代理类生成实例，返回每个目标类-代理实例列表映射
     *
     * @param proxyMap
     * @return
     * @throws Exception
     */
    private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
        Map<Class<?>, List<Proxy>> targetMap = new HashMap<>();
        for (Map.Entry<Class<?>, Set<Class<?>>> entry : proxyMap.entrySet()) {
            Class<?> proxyClass = entry.getKey();
            Set<Class<?>> targetClassSet = entry.getValue();
            for (Class<?> targetClass : targetClassSet) {
                if (!targetMap.containsKey(targetClass)) {
                    ArrayList<Proxy> proxyList = new ArrayList<>();
                    proxyList.add((Proxy) proxyClass.newInstance());
                    targetMap.put(targetClass, proxyList);
                } else {
                    List<Proxy> proxyList = targetMap.get(proxyClass);
                    proxyList.add((Proxy) proxyClass.newInstance());
                }
                LOGGER.debug("load a proxy class(aspect: {}) for {}", proxyClass.getName(), targetClass.getName());
            }
        }
        return targetMap;
    }

}
